package cn.itsource.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.itsource.constants.Constants;
import cn.itsource.mapper.PermissionMapper;
import cn.itsource.pojo.domain.Login;
import cn.itsource.mapper.LoginMapper;
import cn.itsource.pojo.dto.LoginAdminDto;
import cn.itsource.pojo.dto.WxMinAppLoginDTO;
import cn.itsource.pojo.vo.LoginContext;
import cn.itsource.pojo.vo.LoginVO;
import cn.itsource.service.ILoginService;
import cn.itsource.template.WxTemplate;
import cn.itsource.utils.AssertUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * <p>
 * 登录表 服务实现类
 * </p>
 *
 * @author ????
 * @since 2024-06-15
 */
@Service
@Slf4j
public class LoginServiceImpl extends ServiceImpl<LoginMapper, Login> implements ILoginService {

    @Autowired
    private WxTemplate wxTemplate;

    @Autowired
    private PermissionMapper permissionMapper;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * 微信小程序登录
     * 1.先获取OpenId
     * 2.然后根据OpenId查询Login
     * 3.如果为空：就去注册
     * 4.执行登录，创建Token，返回Token
     * @param dto ：参数 - 授权码
     */
    @Override
    public LoginVO wxMinAppLogin(WxMinAppLoginDTO dto) {
        //1.获取OpenId
        String openId = wxTemplate.getOpenId(dto.getWxCode());

        //2.根据OpenId查询Login
        Login login = selectByOpenIdAndType(openId, dto.getType());

        //3.如果为空：提示去注册
        AssertUtil.isNotNull(login,"请先注册");

        //4.执行登录，创建Token，返回Token
        log.info("执行登录，创建Token");

        return execLogin(login);


    }

    //根据openid查询login
    private Login selectByOpenIdAndType(String openId, Integer type) {
        LambdaQueryWrapper<Login> queryWrapper = new LambdaQueryWrapper<>();
        //where openid =? and type =?
        queryWrapper.eq(Login::getOpenId,openId).eq(Login::getType,type);
        return super.getOne(queryWrapper);
    }


    @Override
    public LoginVO loginAdmin(LoginAdminDto dto) {
        //1.根据用户名查询login
        Login one = super.getOne(new LambdaQueryWrapper<Login>().eq(Login::getUsername, dto.getUsername()));

        //2.为空则报错
        AssertUtil.isNotNull(one,"用户名或密码错误");

        //3.判断密码是否正确
        AssertUtil.isTrue(passwordEncoder.matches(dto.getPassword(),one.getPassword()),"用户名或密码错误");

        //4.执行登录，创建Token，返回Token
        return execLogin(one);
    }

    //执行SaToken登录
    private LoginVO execLogin(Login login) {
        //1.执行登录
        StpUtil.login(login.getId());

        //2.拿到Token
        String tokenValue = StpUtil.getTokenValue();
        log.info("执行登录：tokenValue:{}", tokenValue);

        //3.加载登录用户的权限
        List<String> strings = permissionMapper.selectByLoginId(login.getId());

        //4.将权限缓存进Redis
        String format = String.format(Constants.Redis.KEY_PREFIX_PERMISSIONS, login.getId());
        redisTemplate.opsForValue().set(format, strings);
        log.info("加载登录用户的权限到Redis：{}", strings);

        return LoginVO.builder()
                .satoken(tokenValue)
                .permissions(strings)
                .login(LoginContext.builder()
                        .nickName(login.getNickName())
                        .phone(login.getPhone())
                        .avatar(login.getAvatar())
                        .admin(login.getAdmin())
                        .build())
                .build();
    }
}
